using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace CloudShot.Utils
{
	/// <summary>
	/// Provides functions to capture the entire screen, or a particular window, and save it to a file.
	/// </summary>
	public static class ScreenCapture
	{
		/// <summary>
		/// Creates an Image object containing a screen shot of the entire desktop
		/// </summary>
		/// <returns></returns>
		public static Image CaptureScreen()
		{
			return CaptureWindow(User32.GetDesktopWindow());
		}

		/// <summary>
		/// Creates an Image object containing a screen shot of a specific window
		/// </summary>
		/// <param name="handle">The handle to the window. (In windows forms, this is obtained by the Handle property)</param>
		/// <returns></returns>
		public static Image CaptureWindow(IntPtr handle)
		{
			// get te hDC of the target window
			IntPtr hdcSrc = User32.GetWindowDC(handle);
			// get the size
			User32.RECT windowRect = new User32.RECT();
			User32.GetWindowRect(handle, ref windowRect);
			int width = windowRect.Right - windowRect.Left;
			int height = windowRect.Bottom - windowRect.Top;
			// create a device context we can copy to
			IntPtr hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
			// create a bitmap we can copy it to,
			// using GetDeviceCaps to get the width/height
			IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, width, height);
			// select the bitmap object
			IntPtr hOld = Gdi32.SelectObject(hdcDest, hBitmap);
			// bitblt over
			Gdi32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, Gdi32.SRCCOPY);
			// restore selection
			Gdi32.SelectObject(hdcDest, hOld);
			// clean up 
			Gdi32.DeleteDC(hdcDest);
			User32.ReleaseDC(handle, hdcSrc);

			// get a .NET image object for it
			Image img = Image.FromHbitmap(hBitmap);
			// free up the Bitmap object
			Gdi32.DeleteObject(hBitmap);

			return img;
		}

		public static Image CaptureWindowClientArea(IntPtr handle)
		{
			// get te hDC of the target window
			IntPtr hdcSrc = User32.GetWindowDC(handle);
			// get the size
			User32.RECT windowRect = new User32.RECT();
			User32.GetWindowRect(handle, ref windowRect);
			int width = windowRect.Right - windowRect.Left;
			int height = windowRect.Bottom - windowRect.Top;
			// create a device context we can copy to
			IntPtr hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
			// create a bitmap we can copy it to,
			// using GetDeviceCaps to get the width/height
			IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, width, height);
			// select the bitmap object
			IntPtr hOld = Gdi32.SelectObject(hdcDest, hBitmap);
			// bitblt over
			User32.PrintWindow(handle, hdcDest, User32.PW_CLIENTONLY);//does the shot of window even if it is covered by another window
			// restore selection
			Gdi32.SelectObject(hdcDest, hOld);
			// clean up 
			Gdi32.DeleteDC(hdcDest);
			User32.ReleaseDC(handle, hdcSrc);

			// get a .NET image object for it
			Image img = Image.FromHbitmap(hBitmap);
			// free up the Bitmap object
			Gdi32.DeleteObject(hBitmap);

			return img;
		}

		public static Bitmap CaptureRectangle(Rectangle windowRect)
		{
			if ((windowRect.Height <= 0) || windowRect.Width <= 0)
				return null;//throw new ArgumentException("The Rectangle has no size", "windowRect");

			IntPtr handle = User32.GetDesktopWindow();

			// get te hDC of the target window
			IntPtr hdcSrc = User32.GetWindowDC(handle);
			// get the size
			//User32.RECT windowRect = new User32.RECT();
			//User32.GetWindowRect(handle, ref windowRect);
			//int width = windowRect.Right - windowRect.Left;
			//int height = windowRect.Bottom - windowRect.Top;
			// create a device context we can copy to
			IntPtr hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
			// create a bitmap we can copy it to,
			// using GetDeviceCaps to get the width/height
			IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, windowRect.Width, windowRect.Height);
			// select the bitmap object
			IntPtr hOld = Gdi32.SelectObject(hdcDest, hBitmap);
			// bitblt over
			Gdi32.BitBlt(hdcDest, 0, 0, windowRect.Width, windowRect.Height,
					hdcSrc, windowRect.Left, windowRect.Top, Gdi32.SRCCOPY);
			// restore selection
			Gdi32.SelectObject(hdcDest, hOld);
			// clean up 
			Gdi32.DeleteDC(hdcDest);
			User32.ReleaseDC(handle, hdcSrc);

			// get a .NET image object for it
			Bitmap img = Image.FromHbitmap(hBitmap);
			// free up the Bitmap object
			Gdi32.DeleteObject(hBitmap);

			return img;
		}

		/// <summary>
		/// Captures a screen shot of a specific window, and saves it to a file
		/// </summary>
		/// <param name="handle"></param>
		/// <param name="filename"></param>
		/// <param name="format"></param>
		public static void CaptureWindowToFile(IntPtr handle, string filename, ImageFormat format)
		{
			Image img = CaptureWindow(handle);
			img.Save(filename, format);
		}

		/// <summary>
		/// Captures a screen shot of the entire desktop, and saves it to a file
		/// </summary>
		/// <param name="filename"></param>
		/// <param name="format"></param>
		public static void CaptureScreenToFile(string filename, ImageFormat format)
		{
			Image img = CaptureScreen();
			img.Save(filename, format);
		}

		public static void CaptureRectangleToFile(Rectangle rect, string filename, ImageFormat format)
		{
			Image img = CaptureRectangle(rect);
			img.Save(filename, format);
		}

		public static Icon CaptureCursorIcon(out Point screenCursorPosition)
		{
			screenCursorPosition = new Point();
			var ci = new User32.CURSORINFO();
			ci.cbSize = Marshal.SizeOf(ci);

			if (User32.GetCursorInfo(out ci))
			{
				if (ci.flags == User32.CURSOR_SHOWING)
				{
					IntPtr hicon = User32.CopyIcon(ci.hCursor);
					User32.ICONINFO icInfo;
					if (User32.GetIconInfo(hicon, out icInfo))
					{
						screenCursorPosition.X = ci.ptScreenPos.x - icInfo.xHotspot;
						screenCursorPosition.Y = ci.ptScreenPos.y - icInfo.yHotspot;

						Icon ic = Icon.FromHandle(hicon);
						return ic;
					}
				}
			}
			return null;
		}
	}
}